package org.zoomdev.zoom.aop.factory;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.NotFoundException;
import org.zoomdev.zoom.aop.utils.ClassBuilder;
import org.zoomdev.zoom.common.exceptions.ZoomException;
import org.zoomdev.zoom.common.utils.Classes;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public abstract class SingleMethodFactory<T, E> {


    private final ClassPool pool;
    private final ClassLoader classLoader;

    private final Method callMethod;
    private final Class<T> interfaceClass;
    private final Map<E, T> map = new ConcurrentHashMap<>();

    public SingleMethodFactory() {
        this(ClassPool.getDefault(), ClassPool.getDefault().getClassLoader());
    }

    public SingleMethodFactory(ClassPool pool) {
        this(pool, pool.getClassLoader());
    }


    public SingleMethodFactory(ClassPool pool, ClassLoader classLoader) {
        this.pool = pool;
        this.classLoader = classLoader;
        Type tt = Classes.getAllParameterizedTypes(getClass())[0];
        if (tt instanceof Class) {
            this.interfaceClass = (Class<T>) tt;
        } else {
            throw new ZoomException("第一个泛型参数" + tt + "不是Class");
        }

        this.callMethod = interfaceClass.getMethods()[0];
    }

    public T create(E param) {
        return map.computeIfAbsent(param, k -> createImpl(param));
    }

    protected T createImpl(E param) {
        String className = getClassName(param);
        Class<?> clazz;
        try {
            clazz = Class.forName(className);
        } catch (ClassNotFoundException e) {
            ClassBuilder builder = new ClassBuilder(classLoader, pool, className);
            try {
                builder.createClass();
                builder.addInterface(interfaceClass);
                builder.addMethod(callMethod, createBody(param));
                clazz = builder.toClass();
            } catch (CannotCompileException | NotFoundException ex) {
                throw new ZoomException(ex);
            }
        }
        return (T) Classes.newInstance(clazz);
    }

    protected abstract String getClassName(E param);


    protected abstract String createBody(E param);
}
